/* -*- Mode: c; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
/*
  RefObject (http://code.google.com/p/refobject/)
  Copyright (C) 2010-2011 MIURA Shirow (sharow)
*/

#include "RefStringInternal.h"
#include <RefObject/RefByteArray.h>
#include <RefObject/RefModule.h>


typedef struct {
    RefObject *mRefByteArray;
    RefObject *mRefAllocator;
} RefString;

static RefTypeObject *gStringTypeObject = NULL;

RefTypeObject **
RefString_GetTypeObject(void)
{
    return &gStringTypeObject;
}

int
RefString_IsValid(RefObject *aRefObject)
{
    return (RefObject_GetTypeObject(aRefObject) == &gStringTypeObject);
}

static void
RefString_Destructor(RefObject *aRefObject)
{
    RefString *string;
    RefObject *refallocator;
    string = RefObject_GetObject(aRefObject);
    RefObject_DecRef(string->mRefByteArray);
    refallocator = string->mRefAllocator;
    RefAllocator_Free(refallocator, aRefObject);
    RefObject_DecRef(refallocator);
}

static int
CString_LengthByZeroTerm(const unsigned char *aString)
{
    const unsigned char *p;
    int i;
    p = aString;
    for (i = 0; *p != '\0'; i++) {
        p += 1;
    }
    return i;
}

static RefObject *
RefString_Create0(unsigned char *aString, int aLength,
                  int aDuplicate, RefObject *aRefAllocator)
{
    RefObject *refobj;
    RefString *string;
    int length;
    refobj = RefAllocator_Alloc(aRefAllocator, RefObject_SizeOf() + sizeof(*string));
    string = RefObject_Construct(refobj, &gStringTypeObject, RefString_Destructor);
    length = aLength;
    if (length == -1) {
        length = CString_LengthByZeroTerm(aString);
    }
    if (aDuplicate) {
        string->mRefByteArray = RefByteArray_CreateDuplicateWithAllocator(aString, length, aRefAllocator);
    } else {
        string->mRefByteArray = RefByteArray_CreateShallowWithAllocator(aString, length, aRefAllocator);
    }
    string->mRefAllocator = aRefAllocator;
    RefObject_IncRef(aRefAllocator);
    return refobj;
}

RefObject *
RefString_CreateShallow(unsigned char *aString, int aLength)
{
    return RefString_Create0(aString, aLength, 0, RefModule_GetSingletonAllocator());
}

RefObject *
RefString_CreateDuplicate(unsigned char *aString, int aLength)
{
    return RefString_Create0(aString, aLength, 1, RefModule_GetSingletonAllocator());
}


RefObject *
RefString_CreateShallowWithAllocator(unsigned char *aString, int aLength, RefObject *aRefAllocator)
{
    return RefString_Create0(aString, aLength, 0, aRefAllocator);
}

RefObject *
RefString_CreateDuplicateWithAllocator(unsigned char *aString, int aLength, RefObject *aRefAllocator)
{
    return RefString_Create0(aString, aLength, 1, aRefAllocator);
}

unsigned char *
RefString_AsString(RefObject *aRefObject)
{
    RefString *string;
    string = ((RefString *)RefObject_GetObject(aRefObject));
    return RefByteArray_AsByteArray(string->mRefByteArray);
}

int
RefString_GetLength(RefObject *aRefObject)
{
    RefString *string;
    string = ((RefString *)RefObject_GetObject(aRefObject));
    return RefByteArray_GetLength(string->mRefByteArray);
}

/* not use
static int
CString_CompareCString(unsigned char *aCString0, unsigned char *aCString1)
{
    unsigned char *p0;
    unsigned char *p1;
    p0 = aCString0;
    p1 = aCString1;
    for (;;) {
        if (*p0 != *p1) {
            return *p0 - *p1;
        }
        if (*p0 == '\0') {
            break;
        }
        p0 += 1;
        p1 += 1;
    }
    return 0;
}
*/

static int
CString_CompareCStringWithLength(unsigned char *aCString0, unsigned char *aCString1,
                                 int aLength)
{
    unsigned char *p0;
    unsigned char *p1;
    int i;
    int length;
    p0 = aCString0;
    p1 = aCString1;
    length = aLength;
    for (i = 0; i < length; i++) {
        if (*p0 != *p1) {
            return *p0 - *p1;
        }
        if (*p0 == '\0') {
            break;
        }
        p0 += 1;
        p1 += 1;
    }
    return 0;
}

int
RefString_CompareRefString(RefObject *aRefString0, RefObject *aRefString1)
{
    RefString *string0;
    RefString *string1;
    string0 = ((RefString *)RefObject_GetObject(aRefString0));
    string1 = ((RefString *)RefObject_GetObject(aRefString1));
    if (RefByteArray_GetLength(string0->mRefByteArray) !=
        RefByteArray_GetLength(string1->mRefByteArray)) {
        return 1;
    }
    return CString_CompareCStringWithLength(RefByteArray_AsByteArray(string0->mRefByteArray),
                                            RefByteArray_AsByteArray(string1->mRefByteArray),
                                            RefByteArray_GetLength(string0->mRefByteArray));
}

int
RefString_CompareCStringWithLength(RefObject *aRefString, unsigned char *aCString, int aLength)
{
    RefString *string;
    string = ((RefString *)RefObject_GetObject(aRefString));
    if (RefByteArray_GetLength(string->mRefByteArray) != aLength) {
        return 1;
    }
    return CString_CompareCStringWithLength(RefByteArray_AsByteArray(string->mRefByteArray),
                                            aCString, aLength);
}

